package com.aiwiown.thymleaf.dialect.processor;

import org.apache.commons.text.StringEscapeUtils;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.engine.*;
import org.thymeleaf.model.AttributeValueQuotes;
import org.thymeleaf.model.IProcessableElementTag;
import org.thymeleaf.processor.element.IElementTagStructureHandler;
import org.thymeleaf.standard.expression.StandardExpressionExecutionContext;
import org.thymeleaf.standard.processor.AbstractStandardExpressionAttributeTagProcessor;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.util.EscapedAttributeUtils;
import org.thymeleaf.util.Validate;

/**
 * 自定义字典标签
 * <p>Title: UnEscapeHtml4Processor</p>
 * <p>Description: </p>
 *
 * @author Lusifer, dbin0123
 * @version 1.0.0
 * @date 2019/10/25 10:52
 */
public class UnEscapeHtml4Processor extends AbstractStandardExpressionAttributeTagProcessor implements IAttributeDefinitionsAware {

    private static final String ATTRIBUTE_NAME = "value";
    private static final int PRECEDENCE = 1000;

    private final boolean removeIfEmpty;
    private final String targetAttrCompleteName;
    private AttributeDefinition targetAttributeDefinition;

    public UnEscapeHtml4Processor(String dialectPrefix) {
        this(TemplateMode.HTML, dialectPrefix, ATTRIBUTE_NAME, PRECEDENCE, false, false);
    }

    protected UnEscapeHtml4Processor(TemplateMode templateMode, String dialectPrefix, String attrName, int precedence, boolean removeIfEmpty) {
        this(templateMode, dialectPrefix, attrName, attrName, precedence, removeIfEmpty);
    }

    protected UnEscapeHtml4Processor(TemplateMode templateMode, String dialectPrefix, String attrName, int precedence, boolean removeIfEmpty, boolean restrictedExpressionExecution) {
        this(templateMode, dialectPrefix, attrName, attrName, precedence, removeIfEmpty, restrictedExpressionExecution);
    }

    protected UnEscapeHtml4Processor(TemplateMode templateMode, String dialectPrefix, String attrName, int precedence, boolean removeIfEmpty, StandardExpressionExecutionContext expressionExecutionContext) {
        this(templateMode, dialectPrefix, attrName, attrName, precedence, removeIfEmpty, expressionExecutionContext);
    }

    protected UnEscapeHtml4Processor(TemplateMode templateMode, String dialectPrefix, String attrName, String targetAttrCompleteName, int precedence, boolean removeIfEmpty) {
        super(templateMode, dialectPrefix, attrName, precedence, false);
        Validate.notNull(targetAttrCompleteName, "Complete name of target attribute cannot be null");
        this.targetAttrCompleteName = targetAttrCompleteName;
        this.removeIfEmpty = removeIfEmpty;
    }

    protected UnEscapeHtml4Processor(TemplateMode templateMode, String dialectPrefix, String attrName, String targetAttrCompleteName, int precedence, boolean removeIfEmpty, boolean restrictedExpressionExecution) {
        super(templateMode, dialectPrefix, attrName, precedence, false, restrictedExpressionExecution);
        Validate.notNull(targetAttrCompleteName, "Complete name of target attribute cannot be null");
        this.targetAttrCompleteName = targetAttrCompleteName;
        this.removeIfEmpty = removeIfEmpty;
    }

    protected UnEscapeHtml4Processor(TemplateMode templateMode, String dialectPrefix, String attrName, String targetAttrCompleteName, int precedence, boolean removeIfEmpty, StandardExpressionExecutionContext expressionExecutionContext) {
        super(templateMode, dialectPrefix, attrName, precedence, false, expressionExecutionContext);
        Validate.notNull(targetAttrCompleteName, "Complete name of target attribute cannot be null");
        this.targetAttrCompleteName = targetAttrCompleteName;
        this.removeIfEmpty = removeIfEmpty;
    }

    @Override
    public void setAttributeDefinitions(AttributeDefinitions attributeDefinitions) {
        Validate.notNull(attributeDefinitions, "Attribute Definitions cannot be null");
        this.targetAttributeDefinition = attributeDefinitions.forName(this.getTemplateMode(), this.targetAttrCompleteName);
    }

    @Override
    protected final void doProcess(ITemplateContext context, IProcessableElementTag tag, AttributeName attributeName, String attributeValue, Object expressionResult, IElementTagStructureHandler structureHandler) {
        String newAttributeValue = EscapedAttributeUtils.escapeAttribute(this.getTemplateMode(), expressionResult == null ? null : expressionResult.toString());
        if (this.removeIfEmpty && (newAttributeValue == null || newAttributeValue.length() == 0)) {
            structureHandler.removeAttribute(this.targetAttributeDefinition.getAttributeName());
            structureHandler.removeAttribute(attributeName);
        } else {
            replaceAttribute(structureHandler, attributeName, this.targetAttributeDefinition, this.targetAttrCompleteName, newAttributeValue == null ? "" : newAttributeValue);
        }
    }

    public static void replaceAttribute(IElementTagStructureHandler structureHandler, AttributeName oldAttributeName, AttributeDefinition attributeDefinition, String attributeName, String attributeValue) {
        if (structureHandler instanceof ElementTagStructureHandler) {
            ((ElementTagStructureHandler)structureHandler).replaceAttribute(oldAttributeName, attributeDefinition, attributeName, StringEscapeUtils.unescapeHtml4(attributeValue), (AttributeValueQuotes)null);
        } else {
            structureHandler.replaceAttribute(oldAttributeName, attributeName, attributeValue);
        }
    }
}